package com.example.walkera.rtsp_android_client.rtspclient.Video;

import android.graphics.ImageFormat;
import android.util.Log;

import com.example.walkera.rtsp_android_client.rtspclient.Stream.RtpStream;
import com.example.walkera.rtsp_android_client.rtspclient.RtspClient;

/**
 *
 */
public abstract class VideoStream extends RtpStream {
    private final static String TAG="VideoStream";
    private final static int NAL_UNIT_TYPE_STAP_A = 24;
    private final static int NAL_UNIT_TYPE_STAP_B = 25;
    private final static int NAL_UNIT_TYPE_MTAP16 = 26;
    private final static int NAL_UNIT_TYPE_MTAP24 = 27;
    private final static int NAL_UNIT_TYPE_FU_A = 28;
    private final static int NAL_UNIT_TYPE_FU_B = 29;

    private final static int MAX_PACKET_FOR_BUFFER=1024;

    /*nal unit header
    +---------------+
    |0|1|2|3|4|5|6|7|
    +-+-+-+-+-+-+-+-+
    |F|NRI|  Type   |
    +---------------+
    */

    /*FU-A,FU-B
    +---------------+
    |0|1|2|3|4|5|6|7|
    +-+-+-+-+-+-+-+-+
    |S|E|R|  Type   |
    +---------------+
    */
    //NAL unit
    private static int naluHeader;//定义rtp后面的nalu header
    private static int nalType;
    private static int fuIndicator;//定义分片单元FU Indicator
    private static int fuHeader; //定义分片单元FU Header
    private static boolean isAssembling=false;//定义是否在组包过程

    protected byte[] NALUnit;
    private boolean NALEndFlag;
//    private byte[][] buffer = new byte[1024][];
    private byte[][] buffer = new byte[MAX_PACKET_FOR_BUFFER][];
    private int bufferLength;
    protected RtspClient.SDPInfo mSDPinfo;
    private int packetNum = 0;

    public VideoStream() {
        NALEndFlag = false;
        buffer[0] = new byte[1];
    }

    protected abstract void decodeH264Stream();

    @Override
    protected void recombinePacket(StreamPacks streamPacks) {
        if(streamPacks.pt == 96) H264PacketRecombine(streamPacks);
    }

    //This method is used to get the NAL unit from the RTP packet
    //Then translate the NAL unit to the decoder
    private void H264PacketRecombine(StreamPacks streamPacks) {
        int tmpLen;
        Log.d(TAG, "H264PacketRecombine: streamPacks.seq:"+streamPacks.sequenceNumber);
        //先保存nalu头部
        naluHeader=streamPacks.data[0]&0xff;
        nalType = naluHeader & 0x1F;
        if (streamPacks.data.length>=4)
            Log.d(TAG, "H264PacketRecombine: data[0~4]:"+streamPacks.data[0]+" "+streamPacks.data[1]+" "+streamPacks.data[2]+" "+streamPacks.data[3]);
        switch (nalType) {

            //Single-timeaggregation packet
            case NAL_UNIT_TYPE_STAP_A:
                break;

            //Single-timeaggregation packet
            case NAL_UNIT_TYPE_STAP_B:
                break;

            //Multi-time aggregationpacket
            case NAL_UNIT_TYPE_MTAP16:
                break;

            //Multi-time aggregationpacket
            case NAL_UNIT_TYPE_MTAP24:
                break;

            //Fragmentationunit
            case NAL_UNIT_TYPE_FU_A: {
                fuIndicator = naluHeader;
                if (streamPacks.data.length < 2) {
                    Log.d(TAG, "H264PacketRecombine: packet data length:" + streamPacks.data.length);
                    break;
                }
                byte fu_header=streamPacks.data[1];
                //这里要注意c++中unsigned char到java byte的变化,因为经过测试发现，这个地方会出现负数
//                fuHeader = streamPacks.data[1];
                //fuHeader=fu_header&0x0ff;//进行一次转换
                fuHeader=fu_header;
                Log.d(TAG, "H264PacketRecombine: fuHeader:"+fuHeader+" fu_header"+fu_header);
                switch (fuHeader & 0xe0) {
                    //NAL Unit start packet
                    case 0x80: {
                        Log.d(TAG, "H264PacketRecombine: start packet,frametype:" + ((fuIndicator & 0xe0) | fuHeader & 0x1f));
                        NALEndFlag = false;
                        packetNum = 1;
                        bufferLength = streamPacks.data.length - 1;
                        buffer[1] = new byte[streamPacks.data.length - 1];
                        //buffer[1][0] = (byte) ((streamPacks.data[0] & 0xE0) | (streamPacks.data[1] & 0x1F));
                        //组帧类型头
                        buffer[1][0] = (byte) ((fuIndicator & 0xe0) | fuHeader & 0x1f);
                        System.arraycopy(streamPacks.data, 2, buffer[1], 1, streamPacks.data.length - 2);
                        isAssembling = true;
                        break;
                    }
                    //NAL Unit middle packet
                    case 0x00: {
                        if (packetNum >= MAX_PACKET_FOR_BUFFER || bufferLength <= 0 || !isAssembling) {
                            Log.d(TAG, "H264PacketRecombine: lose middle; packetNum:" + packetNum + " bufferLength:" + bufferLength + " frametype:" + ((fuIndicator & 0xe0) | fuHeader & 0x1f));
                            break;
                        }

                        NALEndFlag = false;
                        packetNum++;
                        bufferLength += streamPacks.data.length - 2;
                        buffer[packetNum] = new byte[streamPacks.data.length - 2];
                        System.arraycopy(streamPacks.data, 2, buffer[packetNum], 0, streamPacks.data.length - 2);
                        isAssembling = true;
                        Log.d(TAG, "H264PacketRecombine: middle packet packetNum:" + packetNum + " bufferLength:" + bufferLength + " data_length:" + streamPacks.data.length);
                        break;
                    }
                    //NAL Unit end packet
                    case 0x40: {
                        Log.d(TAG, "H264PacketRecombine: end packet");
                        if (!isAssembling || packetNum < 1 || bufferLength <= 0) {
                            Log.d(TAG, "H264PacketRecombine: lose packet,frametype:" + ((fuIndicator & 0xe0) | fuHeader & 0x1f));
                            packetNum = 0;
                            bufferLength = 0;
                            break;
                        }
                        NALEndFlag = true;
                        NALUnit = new byte[bufferLength + streamPacks.data.length + 2];
                        NALUnit[0] = 0x00;
                        NALUnit[1] = 0x00;
                        NALUnit[2] = 0x00;
                        NALUnit[3] = 0x01;
                        tmpLen = 4;
                        System.arraycopy(buffer[1], 0, NALUnit, tmpLen, buffer[1].length);
                        tmpLen += buffer[1].length;
                        for (int i = 2; i < packetNum + 1; i++) {
                            System.arraycopy(buffer[i], 0, NALUnit, tmpLen, buffer[i].length);
                            tmpLen += buffer[i].length;
                        }
                        System.arraycopy(streamPacks.data, 2, NALUnit, tmpLen, streamPacks.data.length - 2);
                        packetNum = 0;
                        bufferLength = 0;
                        isAssembling = false;
                        break;
                    }
                    default: {
                        Log.d(TAG, "H264PacketRecombine: can't support the packet,fuIndicator:" + fuIndicator + " fuHeader:" + fuHeader);
                    }
                }
                break;
            }

            //Fragmentationunit
            case NAL_UNIT_TYPE_FU_B:
                break;

            //Single NAL unit per packet
            default: {
                Log.d(TAG, "H264PacketRecombine: single NAL type:" + nalType);
                NALUnit = new byte[4 + streamPacks.data.length];
                NALUnit[0] = 0x00;
                NALUnit[1] = 0x00;
                NALUnit[2] = 0x00;
                NALUnit[3] = 0x01;
                System.arraycopy(streamPacks.data, 0, NALUnit, 4, streamPacks.data.length);
                NALEndFlag = true;
                break;
            }
        }
        if(NALEndFlag){
            decodeH264Stream();
        }
    }

    public void stop() {
        NALUnit = null;
        super.stop();
    }
}
